
#include <QSettings>
#include "chanwatcher.h"
#include "chanmanager.h"
#include "components/chan.h"
#include "components/board.h"
#include "mainwindow.h"
#include "mainwindowcontroller.h"
#include "paths.h"
#include <QtXml>
#include <QFile>
#include <QDir>

namespace Wishmaster
{
	static const char* PropertyChanName = "chan_name";
	static const char* PropertyBoardId = "board_id";

	class SwapThread : public QThread
	{
	public:
		SwapThread(ChanManager* manager, QObject* parent = 0) : QThread(parent), m_manager(manager)
		{
		}

		virtual void run()
		{
			if(m_running)
				return;
			m_running = true;
			try
			{
				log(llDebug, "Swapping");
				setPriority(QThread::LowestPriority);
				m_manager->swap();
			}
			catch(...)
			{
			}
			m_running = false;
		}
		
	private:
		ChanManager* m_manager;
		bool m_running;
	};

	ChanWatcher::ChanWatcher() : m_manager(NULL),
		m_view(NULL)
	{
	}

	ChanWatcher::~ChanWatcher()
	{
	}

	void ChanWatcher::saveSettings()
	{
		QDomDocument doc;
		QDomElement root = doc.createElement("boardsettings");
		foreach(const BoardWatchEntry& entry, m_boardWatches)
		{
			QDomElement el = doc.createElement("setting");
			el.setAttribute("chan_name", entry.params.chanName);
			el.setAttribute("board_id", entry.params.boardId);
			el.setAttribute("refresh", entry.params.refresh ? "true" : "false");
			el.setAttribute("index_only", entry.params.onlyIndex ? "true" : "false");
			el.setAttribute("refresh_period", QString::number(entry.params.refreshPeriod));
			root.appendChild(el);
		}
		doc.appendChild(root);

		QFile file(DefaultPath::storageRoot() + QDir::separator() + "boardsettings.xml");
		file.open(QIODevice::WriteOnly);

		file.write(doc.toByteArray());
		file.close();
	}

	void ChanWatcher::loadSettings()
	{
		QFile file(DefaultPath::storageRoot() + QDir::separator() + "boardsettings.xml");
		if(!file.open(QIODevice::ReadOnly))
			return;

		QDomDocument doc;
		bool success = doc.setContent(file.readAll());
		file.close();
		if(!success)
			return;

		QDomElement root = doc.firstChildElement("boardsettings");
		for(unsigned int i = 0; i < root.childNodes().length(); i++)
		{
			QDomElement el = root.childNodes().at(i).toElement();
			BoardWatchParameters params;
			params.chanName = el.attribute("chan_name");
			params.boardId = el.attribute("board_id");
			params.refresh = el.attribute("refresh") == "true";
			params.onlyIndex = el.attribute("index_only") == "true";
			params.refreshPeriod = el.attribute("refresh_period").toUInt();

			setBoardWatchParameters(params);
		}
	}

	bool ChanWatcher::startTimers()
	{
		if(!m_view)
			return false;
		if(!m_manager)
			return false;
		m_swapThread = new SwapThread(m_manager);
		m_swapTimer.setInterval(20000);
		m_swapTimer.start();
		connect(&m_swapTimer, SIGNAL(timeout()), m_swapThread, SLOT(start()));

		QSettings settings;
		m_watchTimer.stop();
		m_watchTimer.start(1000 * settings.value("watch/interval", 60).toInt());
		connect(&m_watchTimer, SIGNAL(timeout()), this, SLOT(watchRefresh()));

		m_autoRefreshTimer.start(1000 * settings.value("autorefresh/interval", 60).toUInt());
		connect(&m_autoRefreshTimer, SIGNAL(timeout()), this, SLOT(autoRefresh()));

		return true;
	}

	void ChanWatcher::setView(MainWindow* view)
	{
		m_view = view;
	}

	void ChanWatcher::setManager(ChanManager* manager)
	{
		m_manager = manager;
	}

	QTimer* ChanWatcher::createTimer(QObject* parent)
	{
		return new QTimer(parent);
	}

	void ChanWatcher::autoRefresh()
	{
		QSettings settings;
		int policynum = settings.value("autorefresh/policy", rpNothing).toUInt();
		bool indexOnly = settings.value("autorefresh/indexonly", false).toBool();
		log(llDebug, "autoRefresh: indexonly: %d", indexOnly);
		switch(policynum)
		{
			case rpAllChans:
			{
				refreshAllChans(false);
				break;
			}
			case rpCurrentChan:
			{
				if(m_view->controller()->activeChan())
					refreshChan(m_view->controller()->activeChan(), false);
				break;
			}
			case rpCurrentBoard:
			{
				if(m_view->controller()->activeBoard())
					refreshBoard(m_view->controller()->activeBoard());
				break;
			}
			case rpNothing:
			default:
				// Do nothing
				break;
		}

		emit statusMessage("Autorefresh started", 5000);
		m_manager->collectGarbage();
	}

	void ChanWatcher::refreshAllChans(bool ignoreOverrides)
	{
		ChanList* chanlist = m_manager->chanlist();
		for(int chanNum = 0; chanNum < chanlist->chansCount(); chanNum++)
		{
			Chan* chan = chanlist->getChan(chanNum);
			refreshChan(chan, ignoreOverrides);
		}
	}

	void ChanWatcher::refreshChan(Chan* chan, bool ignoreOverrides)
	{
		for(int boardIndex = 0; boardIndex < chan->boardsCount(); boardIndex++)
		{
			Board* board = chan->board(boardIndex);
			if(ignoreOverrides)
			{
				refreshBoard(board);
			}
			else
			{
				if(board->property("donotrefresh").toBool())
					continue;
				if(!board->property("override").toBool())
					refreshBoard(board);
			}
		}
	}

	void ChanWatcher::refreshBoard(Board* board)
	{
		if(isBoardInWatchList(board->getParent()->name(), board->name()))
			return;
		log(llDebug, "refreshing board: %s:%s", qPrintable(board->getParent()->name()), qPrintable(board->name()));
		m_manager->requestBoardIndex(board->getParent()->name(), board->name(), 0); // TODO current page
	}

	void ChanWatcher::destroyBoardWatch(const QString& chanName, const QString& boardId)
	{
		for(QList<BoardWatchEntry>::iterator it = m_boardWatches.begin(); it != m_boardWatches.end(); ++it)
		{
			if(((*it).params.chanName == chanName) && ((*it).params.boardId == boardId))
			{
				if((*it).timer)
				{
					(*it).timer->stop();
					delete (*it).timer;
				}
				m_boardWatches.erase(it);
				return;
			}
		}
	}

	void ChanWatcher::watchRefresh()
	{
		m_view->refreshTabs();
		foreach(const ThreadId& id, m_watchList)
		{
			m_manager->requestThread(id);
		}
	}

	void ChanWatcher::boardWatchTimeout()
	{
		QString chanName = sender()->property(PropertyChanName).toString();
		QString boardId = sender()->property(PropertyBoardId).toString();

		if(chanName.isEmpty() || boardId.isEmpty())
		{
			log(llWarning, "ChanWatcher::boardWatchTimeout: Invalid call");
			return;
		}

		log(llDebug, "watch board timeout: %s:%s", qPrintable(chanName), qPrintable(boardId));

		m_manager->requestBoardIndex(chanName, boardId, 0); // TODO current page
	}

	void ChanWatcher::setBoardWatchParameters(const BoardWatchParameters& params)
	{
		destroyBoardWatch(params.chanName, params.boardId);
		BoardWatchEntry entry;
		entry.params = params;
		if(params.refresh)
		{
			entry.timer = createTimer();
			entry.timer->setInterval(params.refreshPeriod * 1000);
			entry.timer->setProperty(PropertyChanName, params.chanName);
			entry.timer->setProperty(PropertyBoardId, params.boardId);
			connect(entry.timer, SIGNAL(timeout()), this, SLOT(boardWatchTimeout()));
			entry.timer->start();
		}
		else
		{
			entry.timer = 0;
		}
		m_boardWatches.append(entry);
	}

	void ChanWatcher::removeBoardWatchParameters(const QString& chanName, const QString& boardId)
	{
		destroyBoardWatch(chanName, boardId);
	}

	bool ChanWatcher::isBoardInWatchList(const QString& chanName, const QString& boardId) const
	{
		foreach(const BoardWatchEntry& entry, m_boardWatches)
		{
			if((entry.params.chanName == chanName) && (entry.params.boardId == boardId))
				return true;
		}
		return false;
	}

	BoardWatchParameters ChanWatcher::boardWatchParameters(const QString& chanName, const QString& boardId) const
	{
		foreach(const BoardWatchEntry& entry, m_boardWatches)
		{
			if((entry.params.chanName == chanName) && (entry.params.boardId == boardId))
				return entry.params;
		}
		BoardWatchParameters params;
		params.refresh = false;
		params.onlyIndex = false;
		params.refreshPeriod = 0;
		return params;
	}

	void ChanWatcher::addToWatchList(const ThreadId& threadId)
	{
		if(!m_watchList.contains(threadId))
			m_watchList.append(threadId);
	}

	void ChanWatcher::removeFromWatchList(const ThreadId& threadId)
	{
		m_watchList.removeOne(threadId);
	}

	QList<ThreadId> ChanWatcher::watchList() const
	{
		return m_watchList;
	}
}

